#include "stdafx.h"
#include "Socket.hpp"

#include <string>

#include <windows.h>
#include <WinSock2.h>

using namespace HpsFramework::Network;


HpsFramework::Network::Address::Address()
{
	this->setAny();
}

HpsFramework::Network::Address::Address( Port port )
{
	setAny();
	this->_address.sin_port = htons(port);
}

HpsFramework::Network::Address::Address( std::string& addr )
{
	setAny();
	this->setAddress(addr);
}

HpsFramework::Network::Address::Address( char* addr )
{
	setAny();
	this->setAddress(std::string(addr));
}

HpsFramework::Network::Address::Address( const Address& other )
{
	this->_address.sin_family = other._address.sin_family;
	this->_address.sin_addr.s_addr = other._address.sin_addr.s_addr;
	this->_address.sin_port = other._address.sin_port;
}

HpsFramework::Network::Address::Address( const AddressType& other )
{
	this->_address.sin_family = other.sin_family;
	this->_address.sin_addr.s_addr = other.sin_addr.s_addr;
	this->_address.sin_port = other.sin_port;
}

HpsFramework::Network::Address::Address( const InAddress& other )
{
	this->_address.sin_addr.s_addr = other.s_addr;
}

void HpsFramework::Network::Address::setAny()
{
	this->_address.sin_family = AF_INET;
	this->_address.sin_addr.s_addr = htonl(INADDR_ANY);
	this->_address.sin_port = 0;
}

HpsFramework::Network::Address::~Address()
{

}

Address& HpsFramework::Network::Address::operator=( const Address &other )
{
	this->_address.sin_family = other._address.sin_family;
	this->_address.sin_addr.s_addr = other._address.sin_addr.s_addr;
	this->_address.sin_port = other._address.sin_port;
	return *this;
}

Address& HpsFramework::Network::Address::operator=( const std::string& other )
{
	this->setAddress(other);
	return *this;
}

Address& HpsFramework::Network::Address::operator=( const char* other )
{
	this->setAddress(other);
	return *this;
}

Address& HpsFramework::Network::Address::operator=( const InAddress other )
{
	this->_address.sin_addr.s_addr = other.s_addr;
	return *this;
}

Address& HpsFramework::Network::Address::operator=( const Port other )
{
	this->_address.sin_port = htons(other);
	return *this;
}

Address& HpsFramework::Network::Address::operator=( const AddressType other )
{
	this->_address.sin_addr.s_addr = other.sin_addr.s_addr;
	return *this;
}

bool HpsFramework::Network::Address::operator==( const Address &other ) const
{
	return (
		(_address.sin_family == other._address.sin_family) &&
		(_address.sin_addr.s_addr == other._address.sin_addr.s_addr) &&
		(_address.sin_port == other._address.sin_port)
		);
}

bool HpsFramework::Network::Address::operator==( const std::string& other ) const
{
	Address::AddressType cmpaddr;
	RawAddress addr = inet_addr(other.c_str());

	if(addr == INADDR_NONE)
	{
		struct hostent* hp = NULL;
		hp = gethostbyname(other.c_str());
		if(hp == NULL)
		{
			return false;
		}
		else
		{
			memcpy(&(cmpaddr.sin_addr), hp->h_addr_list[0], (unsigned int)hp->h_length);  
		}
	}
	else
	{
		memcpy(&cmpaddr.sin_addr, &addr, sizeof(addr));
	}

	return (
		(_address.sin_family == cmpaddr.sin_family) &&
		(_address.sin_addr.s_addr == cmpaddr.sin_addr.s_addr)
		);
}

bool HpsFramework::Network::Address::operator==( const InAddress other ) const
{
	return (_address.sin_addr.s_addr == other.s_addr);
}

bool HpsFramework::Network::Address::operator==( const AddressType other ) const
{
	return (other.sin_addr.s_addr == this->_address.sin_addr.s_addr);
}

void HpsFramework::Network::Address::setPort( Port port )
{
	this->_address.sin_port = htons(port);
}

HpsFramework::Network::Address::Port HpsFramework::Network::Address::getPort()
{
	return htons(this->_address.sin_port);
}

std::string HpsFramework::Network::Address::getAddress()
{
	std::string address = inet_ntoa(*(in_addr*)&this->_address.sin_addr);
	return address;
}

std::string HpsFramework::Network::Address::getCompleteAddress()
{
	char port[10] = {0};
	_itoa_s<10>(htons(this->_address.sin_port), port, 10);

	std::string address = "";
	address.append(inet_ntoa(*(in_addr*)&this->_address.sin_addr));
	address.append(":");
	address.append(port);

	return address;
}

HpsFramework::Network::Address::RawAddress HpsFramework::Network::Address::getRawAddress()
{
	return this->_address.sin_addr.s_addr;
}

void HpsFramework::Network::Address::setAddress( const std::string& newHost )
{
	std::string host = newHost;
	std::string::size_type pos = newHost.find_last_of(':');

	if(std::string::npos != pos && pos < newHost.length())
	{

		int iport = -1;

		std::string port = newHost.substr(pos+1);
		iport = atoi(port.c_str());

		host = newHost.substr(0, pos);

		if(iport != -1)
		{
			this->_address.sin_port = htons(iport);
		}
		else
		{
			HPS_THROW(BadAddressFormat, 0);
			return;
		}

	}

	RawAddress addr = inet_addr(host.c_str());

	if(addr == INADDR_NONE)
	{
		struct hostent* hp = NULL;
		hp = gethostbyname(host.c_str());
		if(hp == NULL)
		{
			HPS_THROW(InvalidAddress, 0);
			return;
		}
		else
		{
			memcpy(&(_address.sin_addr), hp->h_addr_list[0], (unsigned int)hp->h_length);  
		}
	}
	else
	{
		memcpy(&_address.sin_addr, &addr, sizeof(addr));
	}
}

std::string HpsFramework::Network::Address::operator()()
{
	return this->getCompleteAddress();
}

HpsFramework::Network::Address::AddressType& HpsFramework::Network::Address::getAddressInformation()
{
	return this->_address;
}

HpsFramework::Network::Socket::Socket() : _address(), _socket(INVALID_SOCKET)
{
	this->_resetFlags();
}

HpsFramework::Network::Socket::Socket( SOCKET other ) : _socket(other), _address()
{
	this->_resetFlags();
	this->_refreshAddress();
}

HpsFramework::Network::Socket::Socket( Address& other ) : _address(other), _socket(INVALID_SOCKET)
{
	this->_resetFlags();
}

SOCKET HpsFramework::Network::Socket::operator()()
{
	return this->_socket;
}

void HpsFramework::Network::Socket::setReuseAddress( AddressMode reuse )
{
	this->_setAddressReuse = true;
	this->_addressReuse = reuse;
}

void HpsFramework::Network::Socket::setBlockingMode( BlockingMode mode )
{
	this->_setBlocking = true;
	this->_blocking = mode;
}

void HpsFramework::Network::Socket::setIncomingBufferSize( int len )
{
	this->_setInBufferSize = true;
	this->_inBufferSize = len;
}

SOCKET HpsFramework::Network::Socket::getSocket()
{
	return this->_socket;
}

Address& HpsFramework::Network::Socket::getAddress()
{
	return this->_address;
}

void HpsFramework::Network::Socket::setAddress( Address& address )
{
	this->_address = address;
}

void HpsFramework::Network::Socket::close( bool shutdn /*= true */ )
{
	if(this->isValid())
	{
		if ( shutdn ) 
		{
			shutdown(this->getSocket(), SD_BOTH);
		}
		closesocket(this->getSocket());
	}
	this->_socket = INVALID_SOCKET;
}

bool HpsFramework::Network::Socket::isValid()
{
	return (this->_socket != INVALID_SOCKET) && (this->_socket != SOCKET_ERROR);
}

void HpsFramework::Network::Socket::_resetFlags()
{
	this->_setAddressReuse = false;
	this->_setBlocking = false;
	this->_setInBufferSize = false;
	this->_setOutBufferSize = false;
}

bool HpsFramework::Network::Socket::_refreshAddress()
{
	Address::AddressType name; 
	int name_len = sizeof(name);
	memset(&name, 0, name_len);
	getpeername(this->_socket, (sockaddr*)&name, &name_len);
	this->_address = Address(name);
	return true;
}

bool HpsFramework::Network::Socket::_setIncomingBufferSize()
{
	if(!this->_setInBufferSize)
		return true;

	if (setsockopt(_socket, (int) SOL_SOCKET, (int) SO_RCVBUF, (char *)&this->_inBufferSize, sizeof(int))) 
	{
		return false;
	}

	return true;
}

bool HpsFramework::Network::Socket::_setOutgoingBufferSize()
{
	if(!this->_setOutBufferSize)
		return true;

	if (setsockopt(_socket, (int) SOL_SOCKET, (int) SO_SNDBUF, (char *)&this->_outBufferSize, sizeof(int))) 
	{
		return false;
	}

	return true;
}

bool HpsFramework::Network::Socket::_setReuseAddress()
{
	if(!this->_setAddressReuse)
		return true;

	int bOption = this->_addressReuse;
	if (setsockopt(this->getSocket(), SOL_SOCKET, SO_REUSEADDR, (const char *)&bOption, sizeof(bOption)) != 0) {
		return false;
	}

	return true;
}

bool HpsFramework::Network::Socket::_setBlockingMode()
{
	if(!this->_setBlocking)
		return true;

	unsigned long val = this->_blocking;
	if(ioctlsocket(this->getSocket(), FIONBIO, &val) != 0) {
		return false;
	}

	return true;
}

void HpsFramework::Network::Socket::_setSocket( SOCKET s )
{
	this->_socket = s;
	this->_refreshAddress();
}

void HpsFramework::Network::Socket::setOutgoingBufferSize( int len )
{
	this->_setOutBufferSize = true;
	this->_outBufferSize = len;
}

bool HpsFramework::Network::Socket::send( byteptr buffer, size len )
{
	return false;
}

size HpsFramework::Network::Socket::receive( byteptr buffer, size len )
{
	return false;
}

bool HpsFramework::Network::Socket::_createSocket()
{
	return false;
}

bool HpsFramework::Network::Socket::_refreshFlags()
{
	return false;
}

HpsFramework::Network::SyncTcpClient::SyncTcpClient() : Socket()
{
	this->_resetFlags();
}

HpsFramework::Network::SyncTcpClient::SyncTcpClient( SocketType other ) : Socket(other)
{
	this->_resetFlags();
	this->_refreshAddress();
}

HpsFramework::Network::SyncTcpClient::SyncTcpClient( Address& other ) : Socket(other)
{
	this->_resetFlags();
}

HpsFramework::Network::SyncTcpClient::~SyncTcpClient()
{
	//
}

void HpsFramework::Network::SyncTcpClient::setDelayMode( DelayMode mode )
{
	this->_setDelay = true;
	this->_delayMode = mode;
}

bool HpsFramework::Network::SyncTcpClient::setLinger( bool active, int interval /*= 0 */ )
{
	this->_setLin = true;
	this->_lingerFlag = active;
	this->_lingerInterval = interval;
	return true;
}

bool HpsFramework::Network::SyncTcpClient::connect( Address& addr )
{
	if(this->_createSocket())
	{
		Address::AddressType remote = addr.getAddressInformation();
		if(::WSAConnect ( this->getSocket(), reinterpret_cast<const struct ::sockaddr*>(&remote), sizeof(remote), NULL, NULL, NULL, NULL ) != 0)
		{
			HPS_THROW(SocketConnectionFailed, WSAGetLastError());
		}
		this->_refreshAddress();
		return true;
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError());
		return false;
	}
}

bool HpsFramework::Network::SyncTcpClient::send( byteptr buffer, size len )
{
	int sent = 0;

	if(!this->isValid())
	{
		HPS_THROW(SocketNotConnected, 0);
		return false;
	}

	for(;;)
	{

		sent = ::send ( this->getSocket(), (const char*)buffer, len, 0 );
		if ( sent == SOCKET_ERROR )
		{
			HPS_THROW(SocketSendError, WSAGetLastError());
			return false;
		}
		else if ( sent == 0 )
		{
			HPS_THROW(SocketConnectionClosed, WSAGetLastError());
			return false;
		}
		else
		{
			len = len - sent;
			buffer = toBytePtr((unsigned long)buffer + (unsigned long)len);
		}

		if(len <= 0)
		{
			return true;
		}

	}

	return false;
}

bool HpsFramework::Network::SyncTcpClient::send( std::string& str, bool terminator /*= true */ )
{
	char trm = 0;
	if(str != "")
	{
		if(this->send(toBytePtr(str.c_str()), str.length()))
		{
			if(terminator)
			{
				if(this->send(toBytePtr(&trm), 1))
				{
					return true;
				}
			}
			else
			{
				return true;
			}
		}
	}
	return false;
}

size HpsFramework::Network::SyncTcpClient::receive( byteptr buffer, size len )
{
	size ret = HPS_RECEIVE_FAILED;
	int recvd = 0;

	if(!this->isValid())
	{
		HPS_THROW(SocketNotConnected, 0);
		return false;
	}

	recvd = ::recv ( this->getSocket(), toCharPtr(buffer), len, 0 );
	if ( recvd == SOCKET_ERROR )
	{
		HPS_THROW(SocketReceiveError, WSAGetLastError());
		return HPS_RECEIVE_FAILED;
	}
	else if ( recvd == 0 )
	{
		HPS_THROW(SocketConnectionClosed, WSAGetLastError());
		return HPS_RECEIVE_FAILED;
	}
	else
	{
		ret = (size)recvd;
	}

	return ret;
}

std::string HpsFramework::Network::SyncTcpClient::receive( bool waitnull /*= true */ )
{
	std::string ret = "";

	if(!this->isValid())
	{
		HPS_THROW(SocketNotConnected, 0);
		return false;
	}

	size recvd = 0;
	bool receiving = true;
	char szBuffer[1024] = { 0 };
	for( ; receiving; )
	{
		memset(szBuffer, 0, sizeof(szBuffer));
		recvd = ::recv ( this->getSocket(), szBuffer, sizeof(szBuffer), 0 );
		if ( recvd == SOCKET_ERROR )
		{
			HPS_THROW(SocketReceiveError, WSAGetLastError());
			return "";
		}
		else if ( recvd == 0 )
		{
			this->close();
			return ret;
		}
		else
		{
			if(waitnull)
			{
				for(size i = 0; i < recvd; i++)
				{
					if(szBuffer[i] != 0)
					{
						ret += szBuffer[i];
					}
					else
					{
						receiving = false;
						break;
					}
				}
			}
			else
			{
				for(size i = 0; i < recvd; i++)
				{
					ret += szBuffer[i];
				}
				receiving = false;
				break;
			}
		}
	}

	return ret;
}

void HpsFramework::Network::SyncTcpClient::_resetFlags()
{
	this->_setAddressReuse = false;
	this->_setInBufferSize = false;
	this->_setOutBufferSize = false;
	this->_setLin = false;
	this->_setDelay = false;
	this->setBlockingMode(Socket::Blocking);
}

bool HpsFramework::Network::SyncTcpClient::_refreshFlags()
{
	if(!this->_setIncomingBufferSize())
	{
		HPS_THROW(SocketIncomingBufferError, WSAGetLastError());
		return false;
	}
	if(!this->_setOutgoingBufferSize())
	{
		HPS_THROW(SocketOutgoingBufferError, WSAGetLastError());
		return false;
	}
	if(!this->_setBlockingMode())
	{
		HPS_THROW(SocketBlockingModeError, WSAGetLastError());
		return false;
	}
	if(!this->_setDelayMode())
	{
		HPS_THROW(SocketDelayModeError, WSAGetLastError());
		return false;
	}
	if(!this->_setLinger())
	{
		HPS_THROW(SocketLingerError, WSAGetLastError());
		return false;
	}
	return true;
}

bool HpsFramework::Network::SyncTcpClient::_createSocket()
{
	if(this->isValid())
	{
		HPS_THROW(SocketAlreadyCreated, 0);
		return false;
	}

	this->_setSocket(WSASocket(AF_INET, SOCK_STREAM, NULL, NULL, 0, NULL));
	if(this->isValid())
	{
		if(this->_refreshFlags())
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError());
		return false;
	}
}

bool HpsFramework::Network::SyncTcpClient::_setDelayMode()
{
	if(!this->_setDelay)
		return true;

	int nodel = this->_delayMode;
	if (setsockopt(this->getSocket(), IPPROTO_TCP, TCP_NODELAY, (char *) & nodel, sizeof(nodel)) != 0)
	{
		return false;
	}

	return true;
}

bool HpsFramework::Network::SyncTcpClient::_setLinger()
{
	if(!this->_setLin)
		return true;

	linger ll;
	ll.l_linger = this->_lingerInterval;
	ll.l_onoff = this->_lingerFlag;

	if (setsockopt(this->getSocket(), SOL_SOCKET, SO_LINGER, (const char *) & ll, sizeof(linger)) != 0)
	{
		return false;
	}

	return true;
}

HpsFramework::Network::AsyncTcpClientWorker::AsyncTcpClientWorker( AsyncTcpClient* lpClient )
{

}

int HpsFramework::Network::AsyncTcpClientWorker::run()
{
	if(this->_client)
	{
		while(this->wait(0))
		{
			if(this->_client->isValid())
			{
				this->_client->work();
			}
			else
			{
				return 0;
			}
		}
		return 0;
	}
	return 1;
}

void HpsFramework::Network::AsyncTcpClientWorker::setObject( AsyncTcpClient* client )
{
	this->_client = client;
}

HpsFramework::Network::AsyncTcpClient::AsyncTcpClient() : SyncTcpClient(), _worker(NULL)
{
	this->_worker.setObject(this);
	this->_resetFlags();
}

HpsFramework::Network::AsyncTcpClient::AsyncTcpClient( SOCKET other ) : SyncTcpClient(other), _worker(NULL)
{
	this->_worker.setObject(this);
	this->_resetFlags();
	this->_refreshAddress();
}

HpsFramework::Network::AsyncTcpClient::AsyncTcpClient( Address& other ) : SyncTcpClient(other), _worker(NULL)
{
	this->_worker.setObject(this);
	this->_resetFlags();
}

HpsFramework::Network::AsyncTcpClient::~AsyncTcpClient()
{

}

bool HpsFramework::Network::AsyncTcpClient::connect( Address& addr )
{
	if(this->_createSocket())
	{
		Address::AddressType remote = addr.getAddressInformation();
		if(::WSAConnect ( this->getSocket(), reinterpret_cast<const struct ::sockaddr*>(&remote), sizeof(remote), NULL, NULL, NULL, NULL ) != 0)
		{
			int gle = WSAGetLastError();
			if(gle != WSAEWOULDBLOCK)
			{
				HPS_THROW(SocketConnectionFailed, WSAGetLastError());
				return false;
			}
		}
		this->_refreshAddress();
		this->_worker.start();
		return true;
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError());
		return false;
	}
}

void HpsFramework::Network::AsyncTcpClient::close( bool shutdn /*= true */ )
{
	SyncTcpClient::close(shutdn);
	WSACloseEvent(this->_event);
	this->_event = NULL;
}

void HpsFramework::Network::AsyncTcpClient::work()
{

}

void HpsFramework::Network::AsyncTcpClient::_resetFlags()
{
	this->_setAddressReuse = false;
	this->_setInBufferSize = false;
	this->_setOutBufferSize = false;
	this->_setLin = false;
	this->_setDelay = false;
	this->setBlockingMode(Socket::NonBlocking);
}

bool HpsFramework::Network::AsyncTcpClient::_createSocket()
{
	if(this->isValid())
	{
		HPS_THROW(SocketAlreadyCreated, 0);
		return false;
	}

	this->_setSocket(WSASocket(AF_INET, SOCK_STREAM, NULL, NULL, 0, 0));
	if(this->isValid())
	{
		if(this->_refreshFlags())
		{
			if(this->_event != NULL)
			{
				WSACloseEvent(this->_event);
			}
			this->_event = WSACreateEvent();
			WSAEventSelect ( this->getSocket(), this->_event, FD_CONNECT | FD_CLOSE | FD_READ | FD_WRITE );
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError());
		return false;
	}
}

bool HpsFramework::Network::ServerSocket::listen( Address& other, int backlog /*= 100*/ )
{

	if(!this->isValid ())
	{
		return false;
	}

	Address::AddressType addr = other.getAddressInformation();
	int ret = bind ( this->getSocket (), (SOCKADDR*)&addr, sizeof(sockaddr) );

	if(ret == SOCKET_ERROR)
	{
		this->close ();
		HPS_THROW(SocketBindFailed, WSAGetLastError());
		return false;
	}

	ret = ::listen(this->getSocket(), backlog);
	if(ret == SOCKET_ERROR)
	{
		this->close();
		HPS_THROW(SocketListenFailed, WSAGetLastError());
		return false;
	}

	return true;

}

bool HpsFramework::Network::ServerSocket::create( int flags /*= 0*/ )
{

	this->_flags = flags;

	if(this->isValid())
	{
		HPS_THROW(SocketAlreadyCreated, 0);
		return false;
	}

	this->_setSocket ( WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, flags) );
	if(this->isValid ())
	{
		if(this->_refreshFlags())
		{
			return true;
		}
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError ());
		return false;
	}

	return false;

}

bool HpsFramework::Network::ServerSocket::_createSocket()
{

	if(this->isValid())
	{
		HPS_THROW(SocketAlreadyCreated, 0);
		return false;
	}

	this->_setSocket ( WSASocket(AF_INET, SOCK_STREAM, 0, NULL, 0, this->_flags) );
	if(this->isValid())
	{
		if(this->_refreshFlags())
		{
			return true;
		}
		else
		{
			return false;
		}
	}
	else
	{
		HPS_THROW(SocketCreationFailed, WSAGetLastError());
		return false;
	}

}

bool HpsFramework::Network::ServerSocket::_refreshFlags()
{
	if(!this->_setIncomingBufferSize())
	{
		HPS_THROW(SocketIncomingBufferError, WSAGetLastError());
		return false;
	}
	if(!this->_setOutgoingBufferSize())
	{
		HPS_THROW(SocketOutgoingBufferError, WSAGetLastError());
		return false;
	}
	if(!this->_setBlockingMode())
	{
		HPS_THROW(SocketBlockingModeError, WSAGetLastError());
		return false;
	}
	return true;
}

bool HpsFramework::Network::ServerSocket::send( byteptr buffer, size len )
{
	return true;
}

size HpsFramework::Network::ServerSocket::receive( byteptr buffer, size len )
{
	return len;
}

HpsFramework::Network::ServerSocket::ServerSocket( Address& other ) : Socket(other), _flags(0)
{
	this->_refreshFlags ();
}

HpsFramework::Network::ServerSocket::ServerSocket( SocketType other ) : Socket(other), _flags(0)
{
	this->_refreshFlags ();
	this->_refreshAddress ();
}

HpsFramework::Network::ServerSocket::ServerSocket() : Socket(), _flags(0)
{
	this->_refreshFlags ();
}

HpsFramework::Network::ServerSocket::~ServerSocket()
{

}
